/** 
 * @ClassName:     RequestLimitContract.java 
 * @Description:   TODO(用一句话描述该文件做什么)  
 * @author         wangzhe  
 * @Date           2019年1月7日 下午5:07:34  
 */  


package com.huitone.gddw.annotation;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundValueOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class RequestLimitContract {

    private static Logger logger = Logger.getLogger(RequestLimitContract.class);

    @Autowired
    private RedisTemplate newStringRedisTemplate;

    @Around("within(@org.springframework.stereotype.Controller *) && @annotation(limit)")
    public Object requestLimit(final ProceedingJoinPoint joinPoint, RequestLimit limit){
        Map<String, Object> map = new HashMap<>();

        try {
            Object[] args = joinPoint.getArgs();
            HttpServletRequest request = null;
            for(int i = 0; i < args.length; i++){
                if(args[i] instanceof HttpServletRequest){
                    request = (HttpServletRequest) args[i];
                    break;
                }
            }

            if(request == null){
                throw new RuntimeException("request is valid");
            }
            
            
            HttpSession session = request.getSession();
            String remoteAddr = request.getRemoteAddr();
            String url = request.getRequestURI();
            String key = "req_limit_" + url + "_" + remoteAddr;
			BoundValueOperations<String,String> ops = newStringRedisTemplate.boundValueOps(key);
            if(ops.getExpire()==null||ops.getExpire()<0){
                logger.info("remoteAddr" + remoteAddr + ", first request within 1s");
                ops.set("0");
                ops.expire(limit.time(), TimeUnit.MILLISECONDS);
            }
            
            long count;
            try {
                count = Long.valueOf(ops.get());
            } catch (Throwable t){
                count = 0;
            }

            if(count >=limit.count()){
                logger.info("url:\t" + url + ",  remoteAddr:\t" + remoteAddr + " limit request");
                map.put("success", false);
                map.put("message", "the remoteAddr: " + remoteAddr + " is up to the limit " + limit.count() + " within " + limit.time()/1000 + "s");

                return map;
            }
            ops.increment(1);
            return joinPoint.proceed();
        } catch (Throwable t){
            logger.info(t.getMessage(), t);
            throw new RuntimeException(t.getMessage());
        }
    }

}

